package cn.gov.cbrc.east.task;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;


import cn.gov.cbrc.core.util.PropertiesUtil;
import cn.gov.cbrc.east.util.FileTruncate;
import cn.gov.cbrc.east.util.FileUtil;
import cn.gov.cbrc.east.util.StringUtil;
import cn.gov.cbrc.east.verify.LoadFormula;
import cn.gov.cbrc.east.verify.ErrorResults;
import cn.gov.cbrc.east.verify.ResultTotal;
import cn.gov.cbrc.east.verify.S_Table;

/**
 * 项目名称：EastCheck<br>
 * *********************************<br>
 * <P>类名称：Job</P>
 * *********************************<br>
 * <P>类描述：对单个文件进行校验工作</P>
 * 创建人：王川<br>
 * 创建时间：Feb 19, 2016 10:45:12 AM<br>
 * 修改人：王川<br>
 * 修改时间：Feb 19, 2016 10:45:12 AM<br>
 * 修改备注：<br>
 * @version 1.0<br>    
 */
public class Job implements Callable<ResultTotal>{
	private static final Logger logger = LogManager.getLogger(Job.class);
	private String filePath;
	//切割文件大小
	private long truncateSize = 1024*1024*Integer.parseInt(PropertiesUtil.getProperty("firstTruncateSize"));
	//文件中总条数
	private int rows = 0;
	//文件中校验成功条数
	private int totalSuccRows = 0;
	//文件中校验失败条数
	private int totalFailRows = 0;
	//文件中校验错误字段数
	private int totalErrorFields = 0;
	//文件中校验警告字段数
	private int totalWarningFields = 0;
	//切片中错误数
	private ErrorResults result =  new ErrorResults();
	//记录总结果
	ResultTotal total = null;
	private List<FutureTask> futureTaskList2 = new ArrayList<FutureTask>();
	//允许最大错误数
	private int maxErrors = Integer.parseInt(PropertiesUtil.getProperty("maxErrorRows"));
	//处理单个文件线程数
	private int threadNum = Integer.parseInt(PropertiesUtil.getProperty("truncateThreadNums"));
	/** job任务分解线程池 **/
	private ThreadPoolExecutor taskPool = new ThreadPoolExecutor(threadNum, threadNum, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
	
	public Job(String filePath){
		this.filePath = filePath;
	}

	public ResultTotal run() {
		try{
			logger.info(filePath+"开始处理...");
			Date startTime = new Date();
			S_Table table = LoadFormula.getTable(StringUtil.getTableCodeByFileName(filePath));
			//TODO 文件名不符合规范或者是临时表不进行校验,将日志写入文件中
			if(null == table){
				total = new ResultTotal(filePath, false, "文件校验终止，文件名不符合规范。");
				return total ;
			}
			List<FileTruncate> fileTruncateList = FileUtil.truncateFileToTask(filePath, truncateSize);
			//切割文件失败
			if(fileTruncateList == null || fileTruncateList.size() == 0){
				total = new ResultTotal(filePath, false, "文件校验终止，请确认文件字符编码是否为GBK，是否有换行符。");
				return total;
			}
			for(FileTruncate fileTruncate:fileTruncateList){
				//taskPool.execute(new Task(fileTruncate,table));
				Task task = new Task(fileTruncate,table,result,fileTruncateList.size());
				FutureTask<FileTruncate> futureTask = new FutureTask<FileTruncate>(task);
				taskPool.submit(futureTask);
				futureTaskList2.add(futureTask);
			}
			//标记是否中断该文件校验
			boolean isBreak = false;
			while(true){
				//错误行数过多，中断校验
				if(result.get_failRows() > maxErrors){
					total = new ResultTotal(filePath, false, "文件校验终止，错误过多,请确认文件内容是否符合规范。");
					taskPool.shutdownNow();
					isBreak = true;
					break;
				}
				//如果所有future执行完毕，则中断循环
				if(this.allFuturesAreDone(futureTaskList2)) break;
				Thread.sleep(400);
			}
			int finishedNums;
			if(isBreak){
				//如果校验被中断，等待所有任务结束后，获取已处理的碎片数量
				while(!taskPool.isTerminated()){}
				finishedNums=result.getFinishedTruncates();
			}else{
				finishedNums=fileTruncateList.size();
			}
			fileTruncateList.clear();
			for(int i=0;i<finishedNums;i++){
				FutureTask task = futureTaskList2.get(i);
				FileTruncate fileTruncate = (FileTruncate) task.get();
				totalFailRows += fileTruncate.get_failRows();
				totalSuccRows += fileTruncate.get_succRows();
				rows += fileTruncate.getRow();
				totalErrorFields += fileTruncate.get_errorFields();
				totalWarningFields += fileTruncate.get_warningFields();
				fileTruncateList.add(fileTruncate);
				task.cancel(true);
			}
			if(!isBreak){
				int usedTime = (int) ((new Date().getTime()-startTime.getTime())/1000);
				total = new ResultTotal(filePath, rows, totalSuccRows, totalFailRows, usedTime, true, "文件校验结束。",totalErrorFields,totalWarningFields);
			}
			//合并日志
			MergeLogs.Merge(fileTruncateList);
			total.setUsedTime((int) ((new Date().getTime()-startTime.getTime())/1000));
		}catch(Exception e){
			logger.error(e);
		}finally{
			taskPool.shutdown();
		}
		return total;
	}
	/**
	 * 判断所有的Future是否执行结束
	 * @param list
	 * @return
	 */
	boolean allFuturesAreDone(List<FutureTask> list){
		boolean isDone = true;
		for(FutureTask task:list){
			if(!task.isDone()){
				isDone=false;
				break;
			}
		}
		return isDone;
	}
	@Override
	public ResultTotal call() throws Exception {
		return run();
	}
}
